#include "rectangle2.h"
#include "freefuncs.h"


void Rectangle2::addRect (const Rectangle2& other)
{
	int newT, newR, newB, newL; //the new limits of the Rectangle2
	newT = this->getTop() < other.getTop()? this->getTop() : other.getTop(); //then we enlarge this
	newR = this->getRight() < other.getRight()? other.getRight() : this->getRight();
	newB = this->getBottom() < other.getBottom()? other.getBottom() : this->getBottom();
	newL = this->getLeft() < other.getLeft()? this->getLeft() : other.getLeft();
	this->ul.col = newL;
	this->ul.row = newT;
	this->lr.col = newR;
	this->lr.row = newB;
}

int Rectangle2::minDistance (const Coordinate& c) const
{
	switch (this->whichSideIs(c))
	{
	case rlInside:
		return 0;
	case rlTop:
		return (this->getTop() - c.row);
	case rlTopRight:
		return (this->getTop() - c.row) + (c.col - this->getRight());
	case rlRight:
		return (c.col - this->getRight());
	case rlBottomRight:
		return (c.col - this->getRight()) + (c.row - this->getBottom());
	case rlBottom:
		return (c.row - this->getBottom());
	case rlBottomLeft:
		return (c.row - this->getBottom()) + (this->getRight() - c.row);
	case rlLeft:
		return (this->getLeft() - c.col);
	case rlTopLeft:
		return (this->getLeft() - c.col) + (this->getTop() - c.row);
	default:
		cout << "Error calculating Rectangle2::minDistance(). Where the heck is the given point??";
		return -1;
	}
}

//this function returns the square of minDistance, but it is fine because we use it for comparison only, i.e. the absolout number is not used.
float Rectangle2::minDistance_euqleadian (const Coordinate& c) const
{
	float result = 0.0;
	switch (this->whichSideIs(c))
	{
	case rlInside:
		return 0;
	case rlTop:
		return (float)(this->getTop() - c.row);
	case rlTopRight:
		result = (float)intPosPow((this->getTop() - c.row),2) + intPosPow((c.col - this->getRight()),2);
		break;
	case rlRight:
		return (float)(c.col - this->getRight());
	case rlBottomRight:
		result = (float)intPosPow((c.col - this->getRight()),2) + intPosPow((c.row - this->getBottom()),2);
		break;
	case rlBottom:
		return (float)(c.row - this->getBottom());
	case rlBottomLeft:
		result = (float)intPosPow((c.row - this->getBottom()),2) + intPosPow((this->getRight() - c.row),2);
		break;
	case rlLeft:
		return (float)(this->getLeft() - c.col);
	case rlTopLeft:
		result = (float)intPosPow((this->getLeft() - c.col),2) + intPosPow((this->getTop() - c.row),2);
		break;
	default:
		cout << "Error calculating Rectangle2::minDistance(). Where the heck is the given point??";
		return -1;
	}
	return sqrtf(result);
}


void Rectangle2::addCoo (const Coordinate& coo)
{
	
	switch (this->whichSideIs(coo))
	{
	case rlInside:
		break;
	case rlTopLeft:
		this->setUL(coo);
		break;
	case rlTop:
		this->setUL_Row(coo.row);
		break;
	case rlTopRight:
		this->setUL_Row(coo.row);
		this->setLR_Col(coo.col);
		break;
	case rlRight:
		this->setLR_Col(coo.col);
		break;
	case rlBottomRight:
		this->setLR(coo);
		break;
	case rlBottom:
		this->setLR_Row(coo.row);
		break;
	case rlBottomLeft:
		this->setLR_Row(coo.row);
		this->setUL_Col(coo.col);
		break;
	case rlLeft:
		this->setUL_Col(coo.col);
		break;
	default:
		cout << "Error: the point to be added to the Rectangle2 has" <<
			"invalid relative coordinate. Message from Rectangle2::addCoo()" << endl;
	}
}


Rectangle2 Rectangle2::virtualExpand (const Coordinate& coo) const
{
	Rectangle2 copy(*this);
	switch (copy.whichSideIs(coo))
	{
	case rlInside:
		break;
	case rlTopLeft:
		copy.setUL(coo);
		break;
	case rlTop:
		copy.setUL_Row(coo.row);
		break;
	case rlTopRight:
		copy.setUL_Row(coo.row);
		copy.setLR_Col(coo.col);
		break;
	case rlRight:
		copy.setLR_Col(coo.col);
		break;
	case rlBottomRight:
		copy.setLR(coo);
		break;
	case rlBottom:
		copy.setLR_Row(coo.row);
		break;
	case rlBottomLeft:
		copy.setLR_Row(coo.row);
		copy.setUL_Col(coo.col);
		break;
	case rlLeft:
		copy.setUL_Col(coo.col);
		break;
	default:
		cout << "Error: the point to be added to the Rectangle2 has" <<
			"invalid relative coordinate. Message from Rectangle2::virtualExpand()" << endl;
	}
	return copy;
}

void Rectangle2::expand (const Coordinate& coo)
{

	switch (whichSideIs(coo))
	{
	case rlInside:
		break;
	case rlTopLeft:
		setUL(coo);
		break;
	case rlTop:
		setUL_Row(coo.row);
		break;
	case rlTopRight:
		setUL_Row(coo.row);
		setLR_Col(coo.col);
		break;
	case rlRight:
		setLR_Col(coo.col);
		break;
	case rlBottomRight:
		setLR(coo);
		break;
	case rlBottom:
		setLR_Row(coo.row);
		break;
	case rlBottomLeft:
		setLR_Row(coo.row);
		setUL_Col(coo.col);
		break;
	case rlLeft:
		setUL_Col(coo.col);
		break;
	default:
		cout << "Error: the point to be added to the Rectangle2 has" <<
			"invalid relative coordinate. Message from Rectangle2::expand()" << endl;
	}
}

float Rectangle2::minDistance (const Rectangle2& other) const
{
	float minDis = 0.0;

	return minDis;
}

bool Rectangle2::cooIsInside (const Coordinate& c) const
{
	return (c.col >= this->ul.col && c.col <= this->lr.col
		&& c.row >= this->ul.row && c.row <= this->lr.row);
}


bool Rectangle2::cooIsOnLeft (const Coordinate& coo) const
{
	return (coo.col < this->ul.col);
}

bool Rectangle2::cooIsOnRight (const Coordinate& coo) const
{
	return (coo.col > this->lr.col);
}

bool Rectangle2::cooIsOnTop (const Coordinate& coo) const
{
	return (coo.row < this->ul.row);
}

bool Rectangle2::cooIsOnButtom (const Coordinate& coo) const
{
	return (coo.row > this->lr.row);
}

relativeLocation Rectangle2::whichSideIs (const Coordinate& coo) const
{
	relativeLocation result;
	if (cooIsInside (coo))
		result = rlInside;
	else if (cooIsOnTop(coo))
		if (cooIsOnLeft(coo))
			result = rlTopLeft;
		else if (cooIsOnRight(coo))
			result = rlTopRight;
		else
			result = rlTop;
	else if (cooIsOnButtom(coo))
		if (cooIsOnLeft(coo))
			result = rlBottomLeft;
		else if (cooIsOnRight(coo))
			result = rlBottomRight;
		else
			result = rlBottom;
	else
		if (cooIsOnLeft(coo))
			result = rlLeft;
		else if (cooIsOnRight(coo))
			result = rlRight;
	return result;
}

Rectangle2::Rectangle2 (const Coordinate& coo) : ul(coo), lr(coo)
{}

Rectangle2::Rectangle2 (const Coordinate& ul, const Coordinate& lr) : ul(ul), lr(lr)
{}

Rectangle2::Rectangle2 (const Rectangle2& other) : ul (other.ul), lr(other.lr)
{}

Rectangle2::Rectangle2(const Rectangle& rect1) : ul(rect1.getUL()), lr(rect1.getLR())
{}

Rectangle2::Rectangle2 (const LongInstance& li)
{
	if (li.getCooNum() == 0)
	{
		this->setLR(Coordinate(0,0));
		this->setUL(Coordinate(0,0));
		return;
	}

	if (li.getCooNum() == 1)
	{
		this->setLR(li.getCoordinate(0));
		this->setUL(li.getCoordinate(0));
		return;
	}

	this->lr = li.getCoordinate(0);
	this->ul = li.getCoordinate(0);

	Coordinate currentCoo;
	
	for (int i = 1; i < li.getCooNum(); i++)
	{
		currentCoo = li.getCoordinate(i);
		if (currentCoo.row < this->getTop())
			this->setTop(currentCoo.row);
		if (currentCoo.row > this->getBottom())
			this->setBottom(currentCoo.row);
		if (currentCoo.col < this->getLeft())
			this->setLeft(currentCoo.col);
		if (currentCoo.col > this->getRight())
			this->setRight(currentCoo.col);
	}

}

Rectangle2::Rectangle2 (const vector<Coordinate> coos)
{
	if (coos.empty())
	{
		this->setLR(Coordinate(0,0));
		this->setUL(Coordinate(0,0));
		return;
	}

	if ((int) coos.size() == 1)
	{
		this->setLR(coos[0]);
		this->setUL(coos[0]);
		return;
	}

	this->lr = coos[0];
	this->ul = coos[0];

	Coordinate currentCoo;
	
	for (int i = 1; i < static_cast<int>(coos.size()); i++)
	{
		currentCoo = coos[i];
		if (currentCoo.row < this->getTop())
			this->setTop(currentCoo.row);
		if (currentCoo.row > this->getBottom())
			this->setBottom(currentCoo.row);
		if (currentCoo.col < this->getLeft())
			this->setLeft(currentCoo.col);
		if (currentCoo.col > this->getRight())
			this->setRight(currentCoo.col);
	}

}

bool Rectangle2::overlaps (const Rectangle2& other) const
{
	bool result = true;
	if (this->getTop() > other.getBottom() || this->getBottom() < other.getTop()
			|| this->getLeft() > other.getRight() || this->getRight() < other.getLeft())
		result = false;
	return result;
}

void Rectangle2::print(void) const
{
	cout << "Rectangle2's upper left corner: ";
	this->ul.print();
	cout << "; lower right corner: ";
	this->lr.print();
}

ostream& operator<< (ostream& stream, const Rectangle2& rt)
{
	stream << rt.ul << " ~ " << rt.lr;
	return stream;
}

Rectangle2& Rectangle2::operator= (const Rectangle2& other)
{
	if (this == &other)
		return *this;

	this->lr = other.lr;
	this->ul = other.ul;
	return *this;
}